package com.example.questionsystem.modules.qu.util;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.example.questionsystem.core.exception.ServiceException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Arrays;
import java.util.Set;
import java.util.regex.Pattern;

public  class HtmlParserUtils {

    private HtmlParserUtils() {
        throw new UnsupportedOperationException("工具类不可实例化");
    }
    // 支持的题型集合（用于不支持题型校验和报错输出）
    private static final Set<String> SUPPORTED_TYPES = new java.util.LinkedHashSet<>(
            Arrays.asList(
                    "不定项选择题", "判断题", "对错题", "单项选择题", "单选题",
                    "多项选择题", "多选题", "填空题", "计算题", "简答题",
                    "表格题", "综合题", "不定项", "写作题", "作文题"
            )
    );

    public static Document parseHtml(String html) throws IOException {
        return Jsoup.parse(html, "UTF-8");
    }

    // 提取各题型的公共方法（保持对外接口不变）
    public static List<String> extractJudgmentQuestions(Document doc) {
        List<String> result = new ArrayList<>();
        result.addAll(extractQuestions(doc, "判断题"));
        result.addAll(extractQuestions(doc, "对错题"));
        return result;
    }

    public static List<String> extractSingleChoiceQuestions(Document doc) {
        List<String> result = new ArrayList<>();

        result.addAll(extractQuestions(doc, "单选题"));
        result.addAll(extractQuestions(doc, "单项选择题"));
        return result;
    }

    public static List<String> extractBlankQuestions(Document doc) {

        List<String> result = new ArrayList<>();
        result.addAll(extractQuestions(doc, "填空题"));
        return result;
    }
    public static List<String> extractMultipleChoiceQuestions(Document doc) {

        List<String> result = new ArrayList<>();
        result.addAll(extractQuestions(doc, "多项选择题"));
        result.addAll(extractQuestions(doc, "多选题"));
        return result;
    }


    public static List<String> extractTableQuestions(Document doc) {
        return extractQuestions(doc, "表格题");
    }
    public static List<String> extractComprehensiveQuestions(Document doc) {

        List<String> result = new ArrayList<>();
        result.addAll(extractShortAnswerQuestions(doc, "综合题"));

        result.addAll(extractShortAnswerQuestions(doc, "计算题"));
        return result;
    }

    public static List<String> extractShortAnswerQuestions(Document doc) {
        List<String> result = new ArrayList<>();
        result.addAll(extractShortAnswerQuestions(doc, "简答题"));
        result.addAll(extractShortAnswerQuestions(doc, "问答题"));
        return result;
    }
    public static List<String> extractNotChoiceQuestions(Document doc) {
        // 使用 LinkedHashSet 去重且保序
        LinkedHashSet<String> unique = new LinkedHashSet<>();
        unique.addAll(extractQuestions(doc, "不定项选择题")); // 先加更长、更具体的名称
        unique.addAll(extractQuestions(doc, "不定项"));       // 再加短名称（若命中同一块，不会重复）

        return new ArrayList<>(unique);
    }
    public static List<String>  extractWriterQuestions(Document doc) {
        List<String> result = new ArrayList<>();
        result.addAll(extractShortAnswerQuestions(doc, "写作题"));
        result.addAll(extractShortAnswerQuestions(doc, "作文题"));
        return result;
    }
    /**
     * 核心方法：按题型名称提取题目，确保每个题目独立分割
     */
    private static List<String> extractQuestions(Document doc, String sectionName) {
        List<String> questions = new ArrayList<>();
        List<Element> headers = getSectionHeaders(doc);
        Element startHeader = findHeaderBySectionName(headers, sectionName);
        if (startHeader == null) return questions;

        Element endHeader = findNextHeader(headers, startHeader);
        Elements relevantElements = collectElementsBetweenHeaders(doc, startHeader, endHeader);

        StringBuilder currentQuestion = new StringBuilder();
        Pattern questionPattern = Pattern.compile("^[0-9]+[．\\.,，].*"); // 严格匹配 "1. " 格式的题目标识
        boolean foundQuestionStart = false;

        for (Element element : relevantElements) {
            String htmlContent = element.html().trim();

            // 新增HTML空内容检查
            if (htmlContent.isEmpty() || htmlContent.matches("^<\\w+>\\s*</\\w+>$")) {
                continue;
            }
            boolean isNewQuestion = isQuestionStart(element, questionPattern);

            // 发现新题目且当前已有内容 -> 保存前一个题目
            if (isNewQuestion && currentQuestion.length() > 0) {
                questions.add(currentQuestion.toString().trim());
                currentQuestion.setLength(0); // 重置
            }

            // 无论是否新题目，都添加当前元素内容
            currentQuestion.append(element.outerHtml());

            if (isNewQuestion) {
                foundQuestionStart = true;
            }
        }

        // 处理最后一个题目
        if (currentQuestion.length() > 0) {
            questions.add(currentQuestion.toString().trim());
        }

        // 若存在该题型标题，但未发现任何符合编号规则的题目，则抛错
        if (!relevantElements.isEmpty() && !foundQuestionStart) {
            throw new ServiceException(sectionName + "题型未按模板编号（例如：'1.'、'1．'、'1，'），请修正后再识别");
        }

        return questions;
    }
    private static List<String> extractShortAnswerQuestions(Document doc, String sectionName) {
        List<String> questions = new ArrayList<>();
        List<Element> headers = getSectionHeaders(doc);
        Element startHeader = findHeaderBySectionName(headers, sectionName);
        if (startHeader == null) return questions;

        Element endHeader = findNextHeader(headers, startHeader);
        Elements relevantElements = collectElementsBetweenHeaders(doc, startHeader, endHeader);

        StringBuilder currentQuestion = new StringBuilder();
        Pattern questionPattern = Pattern.compile("^【.*】[0-9]+[．\\.,，].*"); // 严格匹配 "【任意字符】1. " 格式的题目标识
        boolean foundQuestionStart = false;

        for (Element element : relevantElements) {
            String text = element.text().trim();
            boolean isNewQuestion = isQuestionStart(element, questionPattern);

            // 发现新题目且当前已有内容 -> 保存前一个题目
            if (isNewQuestion && currentQuestion.length() > 0) {
                questions.add(currentQuestion.toString().trim());
                currentQuestion.setLength(0); // 重置
            }

            // 无论是否新题目，都添加当前元素内容
                currentQuestion.append(element.outerHtml());

            if (isNewQuestion) {
                foundQuestionStart = true;
            }

        }

        // 处理最后一个题目
        if (currentQuestion.length() > 0) {
            questions.add(currentQuestion.toString().trim());
        }

        // 若存在该题型标题，但未发现任何符合编号规则的题目，则抛错
        if (!relevantElements.isEmpty() && !foundQuestionStart) {
            throw new ServiceException(sectionName + "题型未按模板编号（例如：'【题干】1.'），请修正后再识别");
        }

        return questions;
    }
    /**
     * 判断元素是否为题目的起始段落
     */
    private static boolean isQuestionStart(Element element, Pattern pattern) {
        if (!"p".equals(element.tagName())) return false;
        String text = element.text().trim();
        return pattern.matcher(text).find() && !text.matches("^[A-Z]\\..*"); // 排除选项干扰
    }

    // ---------------------- 辅助方法 ----------------------
    /**
     * 获取所有题型标题元素（格式如 "一、判断题"）
     */
    private static List<Element> getSectionHeaders(Document doc) {
        List<Element> headers = new ArrayList<>();
        Pattern pattern = Pattern.compile("^[一二三四五六七八九十百千0-9]*[、，.．:：]?\\s*(不定项选择题|判断题|对错题|单项选择题|单选题|多项选择题|多选题|填空题|计算题|简答题|表格题|综合题|不定项|写作题|作文题)\\b.*");
        for (Element element : doc.select("p")) {
            if (pattern.matcher(element.text().trim()).find()) {
                headers.add(element);
            }
        }
        return headers;
    }

    /**
     * 根据题型名称查找对应的标题元素
     */
    private static Element findHeaderBySectionName(List<Element> headers, String sectionName) {
        for (Element header : headers) {
            String text = header.text().replaceAll("\\s+", "");
            // 去掉前缀编号（如 "一、"、"1." 等）
            text = text.replaceFirst("^[一二三四五六七八九十百千0-9]+[、.．:：，]?", "");

            // 1) 优先精确匹配（等于）
            if (text.equals(sectionName)) {
                return header;
            }
            // 2) 允许后面跟括号/空格/中文冒号等附加说明，但避免短词命中更长词
            if (text.startsWith(sectionName)) {
                String tail = text.substring(sectionName.length());
                // 如果 tail 不是以“选择题”紧接（防止 "不定项" 命中 "不定项选择题"）
                if (!tail.startsWith("选择题")) {
                    // 同时允许 tail 是附加说明（括号/冒号/空白/标点）
                    if (tail.isEmpty() || tail.matches("^[（(：:，,。．.\\s].*")) {
                        return header;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 查找下一个题型标题元素
     */
    private static Element findNextHeader(List<Element> headers, Element currentHeader) {
        int index = headers.indexOf(currentHeader);
        return (index >= 0 && index < headers.size() - 1) ? headers.get(index + 1) : null;
    }

    /**
     * 收集两个标题之间的所有相关元素（p和table）
     */
    private static Elements collectElementsBetweenHeaders(Document doc, Element start, Element end) {
        // 获取所有顶层元素（直接子元素为body的元素）
        Elements topLevelElements = doc.body().children();

        // 找到起始标题和结束标题的索引
        int startIdx = topLevelElements.indexOf(start);
        int endIdx = (end != null) ? topLevelElements.indexOf(end) : topLevelElements.size();

        // 索引无效时返回空集合
        if (startIdx == -1) return new Elements();
        if (endIdx == -1) endIdx = topLevelElements.size();

        // 截取区间内的元素（排除嵌套内容）
        return new Elements(topLevelElements.subList(startIdx + 1, endIdx));
    }

    /**
     * 收集两个标题之间的所有段落元素（全局顺序），用于严格模板校验
     * 该方法不受嵌套容器影响，按照文档内出现顺序截取所有 <p>
     */
    private static Elements collectParagraphsBetweenHeaders(Document doc, Element start, Element end) {
        Elements allParagraphs = doc.select("p");
        int startIdx = allParagraphs.indexOf(start);
        int endIdx = (end != null) ? allParagraphs.indexOf(end) : allParagraphs.size();

        if (startIdx == -1) return new Elements();
        if (endIdx == -1) endIdx = allParagraphs.size();

        return new Elements(allParagraphs.subList(startIdx + 1, endIdx));
    }

    /**
     * 对整份HTML进行模板规则校验：
     * - 至少存在一个合法题型标题（如“一、判断题”）
     * - 每个已出现的题型标题下，至少存在一个符合编号规则的题目
     * 校验失败则抛出 ServiceException
     */
    public static void validateTemplateOrThrow(Document doc) {
        // 先检测是否出现不支持的题型，若有则立即报错
        List<String> unsupported = findUnsupportedSectionTypes(doc);
        if (!unsupported.isEmpty()) {
            throw new ServiceException("发现不支持的题型：" + String.join("、", unsupported) +
                    "；请使用支持的题型：" + String.join("、", SUPPORTED_TYPES));
        }

        List<Element> headers = getSectionHeaders(doc);
        if (headers.isEmpty()) {
            throw new ServiceException("未识别到任何题型标题，请按模板使用规范标题（示例：'一、判断题'、'二、单项选择题'）。 标题需以数字或中文序号加'、' '.' '．' ':' '：'中的任一种后接题型名");
        }

        List<String> errors = new ArrayList<>();
        for (int i = 0; i < headers.size(); i++) {
            Element header = headers.get(i);
            Element next = (i < headers.size() - 1) ? headers.get(i + 1) : null;
            // 归一化标题文本，去掉前缀编号
            String text = header.text().replaceAll("\\s+", "");
            String normalized = text.replaceFirst("^[一二三四五六七八九十百千0-9]+[、.．:：，]?", "");

            // 使用全局段落切片，避免因嵌套导致空集，从而漏掉错误
            Elements relevantElements = collectParagraphsBetweenHeaders(doc, header, next);

            // 根据题型选择编号规则（兼容标题后带括号说明的情况，如："简答题（共2题，共30分）"）
            String baseTitle = normalized.replaceAll("（.*?）", "");
            // 这些题型可能带小题，但也可能没有小题；仅当检测到小题标识时才强制括号格式
            boolean requiresParenSubMarkers = baseTitle.matches("^(简答题|写作题|综合题|作文题)$");
            // 短答类题型整体题目使用【题干】编号形式
            boolean usesBracketShortPattern = baseTitle.matches("^(简答题|问答题|写作题|作文题|综合题|计算题)$");
            Pattern questionPattern = usesBracketShortPattern
                    ? Pattern.compile("^【.*】[0-9]+[．\\.,，].*")
                    : Pattern.compile("^[0-9]+[．\\.,，].*");

            boolean hasStart = false;
            for (Element el : relevantElements) {
                if (isQuestionStart(el, questionPattern)) {
                    hasStart = true;
                    break;
                }
            }

            if (!relevantElements.isEmpty() && !hasStart) {
                String displayTitle = baseTitle; // 报错只显示题型名，不带括号说明
                errors.add(displayTitle + (usesBracketShortPattern
                        ? " 未按模板编号（示例：'【题干】1.'）"
                        : " 未按模板编号（示例：'1.'、'1．'、'1，'）"));
            }

            // 额外校验：当题型支持小题且检测到存在小题标识时，必须使用括号形式
            if (requiresParenSubMarkers && !relevantElements.isEmpty()) {
                Pattern parenMarker = Pattern.compile("^【[\\u4e00-\\u9fa5]+题】[（(]\\s*\\d+\\s*[)）].*");
                Pattern nonParenMarker = Pattern.compile("^【[\\u4e00-\\u9fa5]+题】\\s*\\d+[．\\.,，].*");
                boolean hasParen = false;
                boolean hasNonParen = false;
                for (Element el : relevantElements) {
                    if (!"p".equals(el.tagName())) continue;
                    String txt = el.text().trim();
                    if (parenMarker.matcher(txt).find()) hasParen = true;
                    if (nonParenMarker.matcher(txt).find()) hasNonParen = true;
                }
                // 仅当出现了小题标识且存在非括号格式时才报错；若未出现任何小题标识（整题只有题干），不报错
                if ((hasParen || hasNonParen) && hasNonParen) {
                    errors.add(baseTitle + " 小题标识需为括号形式（示例：'【题目类型】（1）' 或 '【题目类型】(1)'）");
                }
            }
        }

        if (!errors.isEmpty()) {
            throw new ServiceException("题目格式不符合模板：" + String.join("；", errors));
        }
    }

    /**
     * 扫描文档识别可能的题型标题，并找出不受支持的题型名
     */
    private static List<String> findUnsupportedSectionTypes(Document doc) {
        List<String> result = new ArrayList<>();
        // 在段落开头匹配题型名（兼容前缀序号与多种标点），抓取“中文若干 + 题”作为候选
        Pattern candidate = Pattern.compile("^[一二三四五六七八九十百千0-9]*[、，.．:：]?\\s*([\\u4e00-\\u9fa5]+题)\\b.*");
        for (Element p : doc.select("p")) {
            String text = p.text().trim();
            java.util.regex.Matcher m = candidate.matcher(text);
            if (m.find()) {
                String type = m.group(1);
                // 去除括号中说明
                String base = type.replaceAll("（.*?）", "");
                // 若不在支持集合中，且看起来确实是题型（以“题”结尾），则记录
                if (!SUPPORTED_TYPES.contains(base) && base.endsWith("题")) {
                    // 去重
                    if (!result.contains(base)) {
                        result.add(base);
                    }
                }
            }
        }
        return result;
    }



}